package structlearn

import (
	"fmt"
	"unsafe"
)

//大小写决定了结构体的可见性
type Course struct {
	name  string
	price int
	url   string
}

func LearnStruct() {
	//go语言不支持面向对象
	//面向对象的三个基本特称：封装、继承、多态。其他：方法重载，抽象基类
	//定义struct，go语言没有class概念

	//1.实例化-kv形式
	var c Course = Course{
		name:  "django",
		price: 100,
		url:   "https://www.imooc.com",
	}

	//访问
	fmt.Println(c.name, c.price, c.url)

	//大小写在go语言中，决定了变量的可见性
	//一个包中的变量或者结构体，如果首字母是小写，那么对于另一个包是不可见的

	//2、第二种实例化方式，顺序赋值
	c2 := Course{"scrapy", 200, "https://scrapy.com"}
	fmt.Println(c2.name, c2.price, c2.url)

	//3.如果一个指向结构体的指针
	c3 := &Course{"tornado", 100, "https://tornado.com"}
	fmt.Printf("%T\n", c3)
	//取指针运算符，优先级低于点运算符
	fmt.Println((*c3).name, (*c3).price, (*c3).url)
	//可以直接使用结构体的指针点出属性，这是go提供的语法糖，go语言会在内部将指向结构体的指针，转化为*c3这样的形式
	fmt.Println(c3.name, c3.price, c3.url)

	//4.零值,如果不给结构体赋值，go会默认使用零值
	c4 := Course{}
	fmt.Println(c4.name, c4.price, c4.url)

	//5.多种方式零值初始化结构体
	var c5 Course = Course{}
	var c6 Course
	var c7 *Course = new(Course)
	//var c8 *Course
	fmt.Println("零值初始化结构体：")
	//为什么c6和c8不一眼够，c8会报错
	//指针如果只声明，不赋值默认是nil
	//slice, map默认都是nil,所以最好用new声明
	fmt.Println(c5.price)
	fmt.Println(c6.price)
	fmt.Println(c7.price)
	//fmt.Println(c8.price)

	//6.结构体是值类型，赋值的时候会copy
	c9 := Course{"scrapy", 200, "https://scrapy.com"}
	c10 := c9
	fmt.Println(c9)
	fmt.Println(c10)
	c9.price = 10000
	fmt.Println(c9)
	fmt.Println(c10)

	//7.结构体的大小，内存占用的大小
	fmt.Println(unsafe.Sizeof(1))
	//字符串无论多长，占用的内存大小都是16？string的本质是结构体 --> go中内核是结构体的类型，其size都是结构体占用的大小
	fmt.Println(unsafe.Sizeof("scrapy"))
	fmt.Println(unsafe.Sizeof("scrapy-django-scrapy-django-scrapy-django-scrapy-django-scrapy-django-"))
	fmt.Println(unsafe.Sizeof(c9))

	//8.slice的大小
	s1 := []string{"django", "scrapy", "celery"}
	fmt.Println("切片占用的内存", unsafe.Sizeof(s1))

	//9.map的大小
	m1 := map[string]string{
		"django": "python",
		"celery": "django",
		"gin":    "go",
	}
	fmt.Println("map占用的内存", unsafe.Sizeof(m1))

}

//1.结构体的方法，只能和结构体在同一个包中。可以在不同的文 件中
//2.内置的int类型不能加方法，需要的话，需要先定义一个新的类型。type MyInt int
//函数的接受者
func (c *Course) printCourseInfo() {
	fmt.Printf("课程名：%s, 课程价格：%d, 课程地址：%s\n", c.name, c.price, c.url)
}

func (c *Course) setPrice(price int) {
	c.price = price
}

//结构体方法
func StructMethod() {
	c1 := Course{"scrapy", 100, "https://scrapy.com"}
	c1.printCourseInfo()
	//等价于，下面这种调用形式
	//Course.printCourseInfo(c1)

	//如果接收着不是指针，我们修改数据的操作不会成功。我们要把接收者改成指针形。因为结构体是值类型
	c1.setPrice(200)
	//等价于：Course.setPrice(c1, 200)
	c1.printCourseInfo()
}
